#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "png.h"
#include "image.h"
#include "DRMwrap.h"
 
#define PNG_BYTES_TO_CHECK   8
#define WITH_ALPHA           1
#define WITHOUT_ALPHA        0
 
int check_is_png(FILE *fp, const char *filename) //检查是否png文件
{
    if(fp == NULL)
	{
        printf("open failed ...1\n");
        return -1;
    }

    char checkheader[PNG_BYTES_TO_CHECK]; //查询是否png头
    if (fread(checkheader, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) //读取png文件长度错误直接退出
        return 0;
    return png_sig_cmp(checkheader, 0, PNG_BYTES_TO_CHECK); //0正确, 非0错误
}
 
int decode_png(const char *filename, struct image_info *out) //取出png文件中的rgb数据
{
    png_structp png_ptr;  //png文件句柄
    png_infop   info_ptr; //png图像信息句柄

    FILE *fp = fopen(filename, "rb");
    if(fp == NULL)
    {
        printf("打开文件%s失败：%s\n", filename, strerror(errno));
        exit(0);
    }
    if (check_is_png(fp, filename) != 0)
    {
        printf("file is not png ...\n");
		exit(0);
    }
    printf("launcher[%s] ...\n", PNG_LIBPNG_VER_STRING); //打印当前libpng版本号
 
    //1: 初始化libpng的数据结构 :png_ptr, info_ptr
    png_ptr  = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 
    info_ptr = png_create_info_struct(png_ptr);
 
    //2: 设置错误的返回点
    setjmp(png_jmpbuf(png_ptr));
    rewind(fp);
 
    //3: 把png结构体和文件流io进行绑定 
    png_init_io(png_ptr, fp);

    //4:读取png文件信息以及强转转换成RGBA:8888数据格式
    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0); //读取文件信息
    int channels, color_type; 
    channels       = png_get_channels(png_ptr, info_ptr); //通道数量
    color_type     = png_get_color_type(png_ptr, info_ptr);//颜色类型
    out->bpp       = png_get_bit_depth(png_ptr, info_ptr);//位深度    
    out->width     = png_get_image_width(png_ptr, info_ptr);//宽
    out->height    = png_get_image_height(png_ptr, info_ptr);//高
    
    printf("channels = %d color_type = %d bit_depth = %d width = %d height = %d ...\n",
            channels, color_type, out->bpp, out->width, out->height);
 
    int i, j, k;
    int size, pos = 0;
    int temp;
    
    //5: 读取实际的rgb数据
    png_bytepp row_pointers; //实际存储rgb数据的buf
    row_pointers = png_get_rows(png_ptr, info_ptr); //也可以分别每一行获取png_get_rowbytes();
    size = out->width * out->height; //申请内存先计算空间

    //判断是24位还是32位
    if (channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
	{ 
        out->alpha = WITH_ALPHA; //记录是否有透明通道
        size *= (sizeof(unsigned char) * 4); //size = out->width * out->height * channel
        out->rgb = (png_bytep)malloc(size);
        if (NULL == out->rgb) {
            printf("malloc rgba faile ...\n");
            png_destroy_read_struct(&png_ptr, &info_ptr, 0);
            fclose(fp);
            return -1;
        }
        //从row_pointers里读出实际的rgb数据出来
        temp = channels - 1;
        for (i = 0; i < out->height; i++) 
		{
            for (j = 0; j < out->width * 4; j += 4) 
			{
                for (k = temp; k >= 0; k--)
                    out->rgb[pos++] = row_pointers[i][j + k];
			}
		}
    }
    //判断颜色深度是24位还是32位
	else if (channels == 3 || color_type == PNG_COLOR_TYPE_RGB)
	{ 
        out->alpha = WITHOUT_ALPHA;
        size *= (sizeof(unsigned char) * 3);
        out->rgb = (png_bytep)malloc(size);

        if (NULL == out->rgb)
		{
            printf("malloc rgba faile ...\n");
            png_destroy_read_struct(&png_ptr, &info_ptr, 0);
            fclose(fp);
            return -1;
        }

        //从row_pointers里读出实际的rgb数据
        temp = (3 * out->width);
        for (i = 0; i < out->height; i ++)
		{
            for (j = 0; j < temp; j += 3)
			{
                out->rgb[pos++] = row_pointers[i][j+2];
                out->rgb[pos++] = row_pointers[i][j+1];
                out->rgb[pos++] = row_pointers[i][j+0];
            }
        }
    }
    else
        return -1; 

    //6:销毁内存
    png_destroy_read_struct(&png_ptr, &info_ptr, 0);
    fclose(fp);

    //此时， 我们的out->rgba里面已经存储有实际的rgb数据了
    //处理完成以后free(out->rgba)
    return 0;
}
 
void showpng(const char *filename, int lcd, struct drmHandle *drm, int x, int y)
{
    // 解码png，并获取图片信息
    struct image_info out;
    decode_png(filename, &out);

    int w = drm->width<out.width ? drm->width : out.width;
    int h = drm->height<out.height ? drm->height : out.height;

    for(int i=0; i<h; i++)
    {
        int lcd_offset = drm->pitch * i;
        int png_offset = out.bpp/8 * 3 * out.width * i;

        for(int j=0; j<w; j++)
            memcpy(drm->vaddr + lcd_offset + j*4, out.rgb + png_offset + j*3, 3);
    }
}
 
